package io.lhj.chapter05;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeoutException;

import static java.lang.System.currentTimeMillis;
import static java.lang.Thread.currentThread;

public class BooleanLock implements Lock{

    private Thread currentThread;
    private boolean locked = false;
    private final List<Thread> blockedList = new ArrayList<>();


    @Override
    public void lock() throws InterruptedException {
        synchronized (this)//①
        {
            //暂存当前线程
            final Thread tempThread = currentThread();
            try
            {
                if (!blockedList.contains(tempThread))
                    blockedList.add(tempThread);
                this.wait();
            }
            catch(InterruptedException e)
            {
//如果当前线程在wait时被中断，则从blockedList中将其删除，避免内存泄漏
                blockedList.remove(tempThread);
//继续抛出中断异常
                throw e;
            }
        }
    }

    @Override
    public void lock(long mills) throws InterruptedException, TimeoutException {
        synchronized (this)
        {
            if (mills <= 0) //①
            {
                this.lock();
            } else
            {
                long remainingMills = mills;
                long endMills = currentTimeMillis() + remainingMills;
                while (locked)
                {
                    if (remainingMills <= 0) //②
                        throw new TimeoutException("can not get the lock during " + mills);
                    if (!blockedList.contains(currentThread()))
                        blockedList.add(currentThread());
                    this.wait(remainingMills); //③
                    remainingMills = endMills - currentTimeMillis(); //④
                }
                blockedList.remove(currentThread()); //⑤
                this.locked = true;
                this.currentThread = currentThread();
            }
        }
    }

    @Override
    public void unlock() {
        synchronized (this)
        {
            if (currentThread == currentThread())//①
            {
                this.locked = false; // ②
                this.notifyAll(); // ③
            }
        }
    }

    @Override
    public List<Thread> getBlockedThreads() {
        return Collections.unmodifiableList(blockedList);
    }
}
